# ommbot/callbacks.py
#
#

""" implements callbacks """

__copyright__ = 'this file is in the public domain'

import ommbot.generic
import ommbot.dol
import ommbot.thr
import sys
import copy

class Callback(object):

    """ class representing a callback """

    def __init__(self, func, prereq, plugname, kwargs, threaded=True):
        self.func = func
        self.prereq = prereq
        self.plugname = plugname
        self.kwargs = kwargs
        self.threaded = copy.deepcopy(threaded)

class Callbacks(object):

    """ dict of lists containing callbacks """

    def __init__(self):
        self.cbs = ommbot.dol.Dol()

    def size(self):
        """ return nr of callbacks """
        return len(self)

    def add(self, what, func, prereq, kwargs=None, threaded=True):
        """ add a callback """
        what = what.upper()
        # get the plugin this callback was registered from
        plugname = ommbot.generic.calledfrom(sys._getframe())
        # see if kwargs is set if not init to {}
        if not kwargs:
            kwargs = {}
        # add callback to the dict of lists
        self.cbs.add(what, Callback(func, prereq, plugname, 
                         kwargs, threaded))

    def unload(self, plug):
        """ unload plugin """
        unload = []
        # look for all callbacks in a plugin
        for name, cblist in self.cbs.iteritems():
            index = 0
            for item in cblist:
                if item.plugname == plug:
                    unload.append((name, index))
                index += 1
        # delete callbacks
        for callback in unload[::-1]:
            self.delete(callback[0], callback[1])

    def whereis(self, what):
        """ show where callback is registerd """
        result = []
        what = what.upper()
        for name, callback in self.cbs.iteritems():
            if name == what:
                for item in callback:
                    if not item.plugname in result:
                        result.append(item.plugname)
        return result

class Botcallbacks(Callbacks):

    """ callback on ircevent """

    def check(self, bot, ievent):
        """ check for callbacks to be fired """
        # check for "ALL" callbacks
        if self.cbs.has_key('ALL'):
            for cb in self.cbs['ALL']:
                self.callback(cb, bot, ievent)
        cmnd = ievent.cmnd.upper()
        # check for CMND callbacks
        if self.cbs.has_key(cmnd):
            for cb in self.cbs[cmnd]:
                self.callback(cb, bot, ievent)

    def callback(self, cb, bot, ievent):
        # callback cb 
        try:
            # set ievent bot and socket
            ievent.bot = bot
            ievent.sock = bot.sock
            # see if the callback pre requirement succeeds
            if cb.prereq:
                if not cb.prereq(bot, ievent):
                    return
            # check if callback function is there
            if not cb.func:
                return
            # start callback in its own thread
            if cb.threaded:
                ommbot.thr.start_new_thread(cb.func, (bot, ievent), cb.kwargs)
            else:
                cb.func(bot, ievent, **cb.kwargs)
        except Exception, ex:
            ommbot.generic.handle_exception()
